use actix_multipart::Multipart;
use actix_web::{web, App, Error, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::fs::File;
use tokio::io::AsyncRead;
use bytes::Bytes;
use futures::stream::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::ReadBuf;

struct AppState {}

async fn upload_file(
    mut payload: Multipart,
) -> Result<HttpResponse, Error> {
    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_disposition = field.content_disposition();
        let filename = content_disposition
            .get_filename()
            .map(|s| s.to_string())
            .unwrap_or_else(|| "unknown".to_string());

        let filepath = PathBuf::from("uploads").join(&filename);
        let mut file = std::fs::File::create(&filepath)?;

        while let Some(chunk) = field.next().await {
            let data = chunk?;
            file.write_all(&data)?;
        }
    }

    Ok(HttpResponse::Ok().json(serde_json::json!({
        "message": "File uploaded successfully"
    })))
}

struct FileStream {
    file: File,
    buf: Vec<u8>,
}

impl Stream for FileStream {
    type Item = Result<Bytes, std::io::Error>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let this = &mut *self;
        if this.buf.is_empty() {
            this.buf.resize(8192, 0);
        }
        
        let mut buf = ReadBuf::new(&mut this.buf);
        match AsyncRead::poll_read(Pin::new(&mut this.file), cx, &mut buf) {
            Poll::Ready(Ok(())) => {
                let n = buf.filled().len();
                if n == 0 {
                    Poll::Ready(None)
                } else {
                    let chunk = Bytes::copy_from_slice(buf.filled());
                    Poll::Ready(Some(Ok(chunk)))
                }
            }
            Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))),
            Poll::Pending => Poll::Pending,
        }
    }
}

async fn download_file(
    _state: web::Data<Arc<AppState>>,
    filename: web::Path<String>,
) -> Result<HttpResponse, Error> {
    let filepath = PathBuf::from("uploads").join(filename.as_str());
    
    if !filepath.exists() {
        return Ok(HttpResponse::NotFound().json(serde_json::json!({
            "error": "File not found"
        })));
    }

    let file = File::open(&filepath).await?;
    let file_size = file.metadata().await?.len();

    let stream = FileStream {
        file,
        buf: Vec::new(),
    };

    Ok(HttpResponse::Ok()
        .append_header(("Content-Disposition", format!("attachment; filename=\"{}\"", filename.as_str())))
        .append_header(("Content-Length", file_size.to_string()))
        .streaming(stream))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init();

    let upload_dir = PathBuf::from("uploads");
    std::fs::create_dir_all(&upload_dir)?;

    let state = Arc::new(AppState {});

    println!("Server running at http://0.0.0.0:8080");
    println!("Upload directory: {:?}", upload_dir);

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(state.clone()))
            .route("/upload", web::post().to(upload_file))
            .route("/download/{filename}", web::get().to(download_file))
    })
    .bind("0.0.0.0:8080")?
    .run()
    .await
} 